﻿//  -------------------- iSharePoint -------------------- //
// 
//  Company:	IT Complex, LLC
//  Project:	Smart.Common
//  File name:	RulesService.cs
//  Developer:	Solomatov Igor
//  Created:	15.04.2012
// 
//  -------------------- iSharePoint -------------------- //

using System;
using System.Collections.Generic;

namespace Smart.Common.Rules
{
	/// <summary>
	/// Rules service
	/// </summary>
	public abstract class RulesService : IRulesService
	{
		/// <summary>
		/// Current context
		/// </summary>
		public RulesCtx Ctx { get; set; }

		/// <summary>
		/// Rules source
		/// </summary>
		public RulesSource Source { get; set; }

		/// <summary>
		/// Rule checker
		/// </summary>
		public RuleChecker Checker { get; set; }

		/// <summary>
		/// Rule executor
		/// </summary>
		public RuleExecutor Executor { get; set; }

		/// <summary>
		/// Rules service initialization
		/// </summary>
		public virtual void InitService()
		{
			if (Source == null)
				Source = CreateSource();

			if (Checker == null)
				Checker = CreateChecker();

			if (Executor == null)
				Executor = CreateExecutor();
		}

		/// <summary>
		/// Create rules source
		/// </summary>
		/// <returns>Rules source</returns>
		public abstract RulesSource CreateSource();

		/// <summary>
		/// Create rule checker
		/// </summary>
		/// <returns>Rule checker</returns>
		public abstract RuleChecker CreateChecker();

		/// <summary>
		/// Create rule executor
		/// </summary>
		/// <returns>Rule executor</returns>
		public abstract RuleExecutor CreateExecutor();

		/// <summary>
		/// Init context
		/// </summary>
		/// <param name="rulesServiceName">Rules service name</param>
		public virtual void InitContext(string rulesServiceName)
		{
			Ctx =
				new RulesCtx(rulesServiceName)
					{
						Service = this
					};
		}

		/// <summary>
		/// Create new securable filter
		/// </summary>
		public virtual RulesFilter CreateFilter()
		{
			return Source.CreateFilter();
		}

		/// <summary>
		/// Load instance
		/// </summary>
		public virtual IRulesInstance LoadInstance(string instanceId)
		{
			if (instanceId.IsNullOrEmpty()) throw new ArgumentNullException("instanceId");

			return Source.LoadInstance(instanceId);
		}

		/// <summary>
		/// Load instance
		/// </summary>
		public virtual IRulesInstance LoadInstance(RulesFilter filter)
		{
			if (filter == null) throw new ArgumentNullException("filter");

			return Source.LoadInstance(filter);
		}

		/// <summary>
		/// Load instance variables in context
		/// </summary>
		public virtual void LoadInstanceVariables(IRulesInstance instance)
		{
			if (instance == null) throw new ArgumentNullException("instance");

			Ctx.Instance = instance;

			Source.LoadInstanceVariablesToCtx();
		}

		/// <summary>
		/// Check rules for instance
		/// </summary>
		/// <param name="instanceId">Instance identifier</param>
		/// <returns>true, if some rules' actions applied</returns>
		public virtual bool Execute(string instanceId)
		{
			if (instanceId.IsNullOrEmpty()) throw new ArgumentNullException("instanceId");

			Ctx.InstanceId = instanceId;

			return Execute(Source.LoadInstance(instanceId));
		}

		/// <summary>
		/// Check rules for instance
		/// </summary>
		/// <returns>true, if some rules' actions applied</returns>
		public virtual bool Execute<TInstanceType>(TInstanceType instance)
			where TInstanceType : class, IRulesInstance
		{
			if (instance == null) throw new ArgumentNullException("instance");

			Ctx.Instance = instance;

			Source.LoadInstanceVariablesToCtx();
			Source.ParseRulesToCtx(Ctx.Instance.Rules);

			Ctx.StopRulesProcessing = false;

			var res = false;

			for (int I = 0, ICnt = Ctx.Rules.Count; I < ICnt; I++)
			{
				var rule = Ctx.Rules[I];

				if (Checker.CheckRule(rule))
					res |= Executor.ApplyRule(rule);

				if (Ctx.StopRulesProcessing)
					break;
			}

			return res;
		}

		/// <summary>
		/// Check rule's condition
		/// </summary>
		/// <returns>true, if condition is fulfilled and action applied</returns>
		public virtual bool Execute<TRuleType, TInstanceType>(TRuleType rule, TInstanceType instance, IDictionary<string, object> variables)
			where TRuleType : class, IRule
			where TInstanceType : class, IRulesInstance
		{
			if (rule == null) throw new ArgumentNullException("rule");
			if (instance == null) throw new ArgumentNullException("instance");

			Ctx.Instance = instance;
			Ctx.Rules.Add(rule);

			Source.LoadVariablesToCtx(variables);

			return
				Checker.CheckRule(rule) &&
				Executor.ApplyRule(rule);
		}

		/// <summary>
		/// Check rule's condition
		/// </summary>
		/// <returns>true, if condition is fulfilled and action applied</returns>
		public virtual bool Execute<TInstanceType>(object rule, TInstanceType instance, IDictionary<string, object> variables)
			where TInstanceType : class, IRulesInstance
		{
			if (rule == null) throw new ArgumentNullException("rule");
			if (instance == null) throw new ArgumentNullException("instance");

			return
				Execute(Source.ParseRule(rule), instance, variables);
		}

		/// <summary>
		/// Check rule's condition
		/// </summary>
		public virtual bool Check<TRuleConditionType, TInstanceType>(TRuleConditionType ruleCondition, TInstanceType instance, IDictionary<string, object> variables)
			where TRuleConditionType : class, IRuleCondition
			where TInstanceType : class, IRulesInstance
		{
			if (ruleCondition == null) throw new ArgumentNullException("ruleCondition");
			if (instance == null) throw new ArgumentNullException("instance");

			Ctx.Instance = instance;

			Source.LoadVariablesToCtx(variables);

			return
				Checker.CheckCondition(ruleCondition);
		}

		/// <summary>
		/// Check rule's condition
		/// </summary>
		public virtual bool Check<TInstanceType>(object ruleCondition, TInstanceType instance, IDictionary<string, object> variables)
			where TInstanceType : class, IRulesInstance
		{
			if (ruleCondition == null) throw new ArgumentNullException("ruleCondition");
			if (instance == null) throw new ArgumentNullException("instance");

			Ctx.Instance = instance;

			Source.LoadVariablesToCtx(variables);

			return
				Checker.CheckCondition(Source.ParseCondition(ruleCondition));
		}

		/// <summary>
		/// Execute rule's action for instance
		/// </summary>
		public virtual bool Apply<TRuleActionType, TInstanceType>(TRuleActionType ruleAction, TInstanceType instance, IDictionary<string, object> variables)
			where TRuleActionType : class, IRuleAction
			where TInstanceType : class, IRulesInstance
		{
			if (ruleAction == null) throw new ArgumentNullException("ruleAction");
			if (instance == null) throw new ArgumentNullException("instance");

			Ctx.Instance = instance;

			Source.LoadVariablesToCtx(variables);

			return
				Executor.ApplyAction(ruleAction);
		}

		/// <summary>
		/// Execute rule's action for instance
		/// </summary>
		public virtual bool Apply<TInstanceType>(object ruleAction, TInstanceType instance, IDictionary<string, object> variables)
			where TInstanceType : class, IRulesInstance
		{
			if (ruleAction == null) throw new ArgumentNullException("ruleAction");
			if (instance == null) throw new ArgumentNullException("instance");

			Ctx.Instance = instance;

			Source.LoadVariablesToCtx(variables);

			return
				Executor.ApplyAction(Source.ParseAction(ruleAction));
		}
	}
}